﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Linq;

namespace Recipes.SingleModelCrud
{
    /// <summary>
    /// This scenario performs basic CRUD operations on a simple model without children.
    /// </summary>
    /// <typeparam name="TModel">A EmployeeClassification model or entity</typeparam>
    [TestCategory("SingleModelCrud")]
    public abstract class SingleModelCrudTests<TModel> : TestBase
        where TModel : class, IEmployeeClassification, new()
    {
        [TestMethod]
        public void Create_ParameterCheck()
        {
            var repository = GetScenario();
            AssertThrowsException<ArgumentNullException>(() => repository.Create(null!));
        }

        /// <summary>
        /// Create and delete a row.
        /// </summary>
        [TestMethod]
        public void CreateAndDeleteByKey()
        {
            var repository = GetScenario();

            var newRecord = new TModel();
            newRecord.EmployeeClassificationName = "Test " + DateTime.Now.Ticks;
            var newKey = repository.Create(newRecord);
            Assert.IsTrue(newKey >= 1000, "keys under 1000 were not generated by the database");

            repository.DeleteByKey(newKey);

            var all = repository.GetAll();
            Assert.IsFalse(all.Any(ec => ec.EmployeeClassificationKey == newKey));
        }

        /// <summary>
        /// Create and delete a row.
        /// </summary>
        [TestMethod]
        public void CreateAndDeleteByModel()
        {
            var repository = GetScenario();

            var newRecord = new TModel();
            newRecord.EmployeeClassificationName = "Test " + DateTime.Now.Ticks;
            var newKey = repository.Create(newRecord);
            Assert.IsTrue(newKey >= 1000, "keys under 1000 were not generated by the database");

            var echo = repository.GetByKey(newKey);
            Assert.IsNotNull(echo);
            repository.Delete(echo!);

            var all = repository.GetAll();
            Assert.IsFalse(all.Any(ec => ec.EmployeeClassificationKey == newKey));
        }

        /// <summary>
        /// Create and read back a row.
        /// </summary>
        [TestMethod]
        public void CreateAndReadBack()
        {
            var repository = GetScenario();

            var newRecord = new TModel();
            newRecord.EmployeeClassificationName = "Test " + DateTime.Now.Ticks;
            var newKey = repository.Create(newRecord);
            Assert.IsTrue(newKey >= 1000, "keys under 1000 were not generated by the database");

            var echo = repository.GetByKey(newKey);
            Assert.IsNotNull(echo);
            Assert.AreEqual(newKey, echo!.EmployeeClassificationKey);
            Assert.AreEqual(newRecord.EmployeeClassificationName, echo.EmployeeClassificationName);

            var search = repository.FindByName(newRecord.EmployeeClassificationName);
            Assert.IsNotNull(search);
            Assert.AreEqual(newKey, search!.EmployeeClassificationKey);
            Assert.AreEqual(newRecord.EmployeeClassificationName, search.EmployeeClassificationName);
        }

        /// <summary>
        /// Create and update a row.
        /// </summary>
        [TestMethod]
        public void CreateAndUpdate()
        {
            var repository = GetScenario();

            var newRecord = new TModel();
            newRecord.EmployeeClassificationName = "Test " + DateTime.Now.Ticks;
            var newKey = repository.Create(newRecord);
            Assert.IsTrue(newKey >= 1000); //keys under 1000 were not generated by the database

            var echo = repository.GetByKey(newKey);
            Assert.IsNotNull(echo);
            Assert.AreEqual(newRecord.EmployeeClassificationName, echo!.EmployeeClassificationName);
            echo.EmployeeClassificationName = "Updated " + DateTime.Now.Ticks;
            repository.Update(echo);

            var updated = repository.GetByKey(newKey);
            Assert.IsNotNull(updated);
            Assert.AreEqual(echo.EmployeeClassificationName, updated!.EmployeeClassificationName);
        }

        [TestMethod]
        public void Delete_ParameterCheck()
        {
            var repository = GetScenario();
            AssertThrowsException<ArgumentNullException>(() => repository.Delete(null!));
        }

        /// <summary>
        /// Get all rows from a table.
        /// </summary>
        [TestMethod]
        public void GetAll()
        {
            var repository = GetScenario();

            var allRows = repository.GetAll();
            Assert.IsNotNull(allRows);
            Assert.AreNotEqual(0, allRows.Count);

            var row1 = allRows.SingleOrDefault(x => x.EmployeeClassificationKey == 1);
            Assert.IsNotNull(row1);
            Assert.AreEqual(1, row1!.EmployeeClassificationKey);
            Assert.AreEqual("Full Time Salary", row1.EmployeeClassificationName);

            var row4 = allRows.SingleOrDefault(x => x.EmployeeClassificationKey == 4);
            Assert.IsNotNull(row4);
            Assert.AreEqual(4, row4!.EmployeeClassificationKey);
            Assert.AreEqual("Contractor", row4.EmployeeClassificationName);
        }

        /// <summary>
        /// Get a row using a primary key.
        /// </summary>
        [TestMethod]
        public void GetByKey()
        {
            var repository = GetScenario();

            var ec1 = repository.GetByKey(1);
            var ec2 = repository.GetByKey(2);
            var ec3 = repository.GetByKey(3);

            Assert.IsNotNull(ec1);
            Assert.IsNotNull(ec2);
            Assert.IsNotNull(ec3);

            Assert.AreEqual(1, ec1!.EmployeeClassificationKey);
            Assert.AreEqual(2, ec2!.EmployeeClassificationKey);
            Assert.AreEqual(3, ec3!.EmployeeClassificationKey);
        }

        [TestMethod]
        public void Update_ParameterCheck()
        {
            var repository = GetScenario();
            AssertThrowsException<ArgumentNullException>(() => repository.Update(null!));
        }

        protected abstract ISingleModelCrudScenario<TModel> GetScenario();
    }
}
